package edu.yctc.weblog.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.binarywang.java.emoji.EmojiConverter;

import edu.yctc.weblog.constant.ConstantHolder;
import edu.yctc.weblog.constant.ResultCode;
import edu.yctc.weblog.dao.BlogDAO;
import edu.yctc.weblog.dao.BlogTagDAO;
import edu.yctc.weblog.dao.CategoryDAO;
import edu.yctc.weblog.dao.CommentDAO;
import edu.yctc.weblog.dao.TagDAO;
import edu.yctc.weblog.dto.BlogDTO;
import edu.yctc.weblog.dto.BlogDetailDTO;
import edu.yctc.weblog.dto.BlogListDTO;
import edu.yctc.weblog.dto.BlogPageDTO;
import edu.yctc.weblog.entity.BlogDO;
import edu.yctc.weblog.entity.BlogTagDO;
import edu.yctc.weblog.entity.CategoryDO;
import edu.yctc.weblog.entity.PageDO;
import edu.yctc.weblog.entity.ResultDO;
import edu.yctc.weblog.entity.TagDO;
import edu.yctc.weblog.enums.BlogStatus;
import edu.yctc.weblog.enums.CommentStatus;
import edu.yctc.weblog.enums.TagDeleteStatus;
import edu.yctc.weblog.service.BlogService;
import edu.yctc.weblog.utils.CommonUtils;
import edu.yctc.weblog.utils.DTOUtil;
import edu.yctc.weblog.utils.MarkDownUtil;
import edu.yctc.weblog.utils.PageQueryUtil;

@Service("blogService")
public class BlogServiceImpl implements BlogService {

    private final static Logger LOG = LoggerFactory.getLogger("serviceLogger");

    @Resource
    private BlogDAO blogDAO;

    @Resource
    private CategoryDAO categoryDAO;

    @Resource
    private TagDAO tagDAO;

    @Resource
    private BlogTagDAO blogTagDAO;

    @Resource
    private CommentDAO commentDAO;

    @Autowired
    private DTOUtil dtoUtil;

    @Override
    @Transactional
    public ResultDO<Void> saveBlog(BlogDO blogDO) {
        CategoryDO categoryDO = categoryDAO.getCategoryDOById(blogDO.getCategoryId());
        if (categoryDO == null) {
            // 设置为默认分类
            blogDO.setCategoryId(ConstantHolder.ID_OF_DEFAULT_CATEGORY);
        } else {
            // 排序权重增大
            categoryDO.setRank(categoryDO.getRank() + 1);
        }
        // content转换编码
        EmojiConverter emojiConverter = EmojiConverter.getInstance();
        String content = emojiConverter.toHtml(blogDO.getContent());
        blogDO.setContent(content);
        blogDAO.insert(blogDO);
        String subUrl = blogDO.getSubUrl();
        if (StringUtils.isBlank(subUrl) || subUrl == null) {
            blogDO.setSubUrl(blogDO.getId() + "");
            blogDAO.update(blogDO);
        }
        LOG.info("insert blogDo success, blogDO={}", blogDO);
        // 标签
        String[] tags = blogDO.getTags().split(",");
        // 新增标签
        List<TagDO> newTagList = new ArrayList<>();
        List<TagDO> allTagList = new ArrayList<>();
        for (int i = 0; i < tags.length; i++) {
            TagDO tagDO = tagDAO.getTagDOByName(tags[i]);
            if (tagDO == null) {
                // 是新的标签
                TagDO temp = new TagDO();
                temp.setName(tags[i]);
                temp.setIsDeleted(TagDeleteStatus.UNDELETED);
                newTagList.add(temp);
            } else {
                allTagList.add(tagDO);
            }
        }
        // 新增
        if (newTagList.size() > 0) {
            tagDAO.batchInsertBlogTag(newTagList);
        }
        categoryDAO.update(categoryDO);
        LOG.info("update categoryDO success, categoryDO={}", categoryDO);
        List<BlogTagDO> blogTagDOList = new ArrayList<>();
        allTagList.addAll(newTagList);
        for (TagDO tagDO : allTagList) {
            BlogTagDO blogTagDO = new BlogTagDO();
            blogTagDO.setBlogId(blogDO.getId());
            blogTagDO.setTagId(tagDO.getId());
            blogTagDOList.add(blogTagDO);
        }
        blogTagDAO.batchInsert(blogTagDOList);
        LOG.info("save blogTagDOs success, blogTagDOs={}", blogTagDOList);
        return new ResultDO<>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS);
    }

    @Override
    public ResultDO<PageDO> getBlogPage(PageQueryUtil pageQueryUtil) {
        List<BlogDO> blogDOList = blogDAO.getBlogList(pageQueryUtil);
        List<BlogPageDTO> blogPageDTOs = new ArrayList<>();
        for (BlogDO blogDO : blogDOList) {
            BlogPageDTO blogPageDTO = dtoUtil.coverBlogDO2BlogPageDO(blogDO);
            blogPageDTOs.add(blogPageDTO);
        }
        int total = blogDAO.getTotalBlogDOs(pageQueryUtil);
        LOG.info("get blog page success");
        return new ResultDO<>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS,
            new PageDO(total, pageQueryUtil.getLimit(), pageQueryUtil.getPage(), blogPageDTOs));
    }

    @Override
    @Transactional
    public ResultDO<Void> deleteBatch(Long[] ids) {
        blogDAO.deleteBatch(ids);
        LOG.info("delete blogs success, blog ids={}", Arrays.asList(ids));
        return new ResultDO<>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS);
    }

    @Override
    public ResultDO<Integer> getTotalBlogs() {
        return new ResultDO<Integer>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS, blogDAO.getTotalBlogDOs(null));
    }

    @Override
    public ResultDO<BlogDO> getBlogDOById(Long id) {
        BlogDO blogDO = blogDAO.getBlogDOById(id);
        if (blogDO == null) {
            LOG.error("get blogdo by id fail, blogdo does not exist, id={}", id);
            return new ResultDO<BlogDO>(false, ResultCode.DB_ERROR, ResultCode.MSG_DB_ERROR, null);
        }
        return new ResultDO<BlogDO>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS, blogDO);
    }

    @Override
    @Transactional
    public ResultDO<Void> update(BlogDO blogDO) {
        BlogDO toUpdate = blogDAO.getBlogDOById(blogDO.getId());
        if (toUpdate == null) {
            LOG.error("update blogdo fail, blogdo does not exist, id={}", blogDO.getId());
            return new ResultDO<>(false, ResultCode.DB_ERROR, ResultCode.MSG_DB_ERROR);
        }
        EmojiConverter emojiConverter = EmojiConverter.getInstance();
        String content = emojiConverter.toHtml(blogDO.getContent());
        toUpdate.setTitle(blogDO.getTitle());
        toUpdate.setSubUrl(blogDO.getSubUrl());
        toUpdate.setContent(content);
        toUpdate.setCoverImage(blogDO.getCoverImage());
        toUpdate.setStatus(blogDO.getStatus());
        toUpdate.setIsEnableComment(blogDO.getIsEnableComment());
        CategoryDO categoryDO = categoryDAO.getCategoryDOById(blogDO.getCategoryId());
        if (categoryDO == null) {
            toUpdate.setCategoryId(ConstantHolder.ID_OF_DEFAULT_CATEGORY);
        } else {
            toUpdate.setCategoryId(categoryDO.getId());
            categoryDO.setRank(categoryDO.getRank() + 1);
        }
        String[] tags = blogDO.getTags().split(",");
        toUpdate.setTags(blogDO.getTags());
        // 新增标签
        List<TagDO> newTagList = new ArrayList<>();
        List<TagDO> allTagList = new ArrayList<>();
        for (int i = 0; i < tags.length; i++) {
            TagDO tagDO = tagDAO.getTagDOByName(tags[i]);
            if (tagDO == null) {
                // 是新的标签
                TagDO temp = new TagDO();
                temp.setName(tags[i]);
                temp.setIsDeleted(TagDeleteStatus.UNDELETED);
                newTagList.add(temp);
            } else {
                allTagList.add(tagDO);
            }
        }
        // 新增
        if (newTagList.size() > 0) {
            tagDAO.batchInsertBlogTag(newTagList);
        }
        List<BlogTagDO> blogTagDOList = new ArrayList<>();
        allTagList.addAll(newTagList);
        for (TagDO tagDO : allTagList) {
            BlogTagDO blogTagDO = new BlogTagDO();
            blogTagDO.setBlogId(blogDO.getId());
            blogTagDO.setTagId(tagDO.getId());
            blogTagDOList.add(blogTagDO);
        }
        // 修改blog信息->修改分类排序值->删除原关系数据->保存新的关系数据
        categoryDAO.update(categoryDO);
        LOG.info("update categoryDO success, categoryDO={}", categoryDO);
        blogTagDAO.deleteByBlogId(blogDO.getId());
        LOG.info("delete blogTagDO by blog id success, blog id={}", blogDO.getId());
        blogTagDAO.batchInsert(blogTagDOList);
        LOG.info("insert blogTagDO success, blogTagDOs={}", blogTagDOList);
        blogDAO.update(toUpdate);
        LOG.info("update blogDO success, blogDO={}", toUpdate);
        return new ResultDO<>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS);
    }

    @Override
    public ResultDO<PageDO> getBlogForIndexPage(int page) {
        if (page <= 0) {
            LOG.error("get blog for index page fail, parameter invalid, page={}", page);
            return new ResultDO<PageDO>(false, ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID, null);
        }
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("page", page);
        // 每页9条
        params.put("limit", 9);
        // 过滤发布状态下的数据
        params.put("status", BlogStatus.RELEASED);
        PageQueryUtil pageQueryUtil = new PageQueryUtil(params);
        List<BlogDO> blogDOList = blogDAO.getBlogList(pageQueryUtil);
        List<BlogListDTO> blogListDTOs = getBlogListDTOsByBlogs(blogDOList);
        int total = blogDAO.getTotalBlogDOs(pageQueryUtil);
        LOG.info("get blog for index page success");
        return new ResultDO<PageDO>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS,
            new PageDO(total, pageQueryUtil.getLimit(), pageQueryUtil.getPage(), blogListDTOs));
    }

    @Override
    public ResultDO<List<BlogDTO>> getBlogListForIndexPage(int type) {
        if (type != ConstantHolder.TYPE_LASTEST_RELEASED && type != ConstantHolder.TYPE_MOST_CLICK) {
            LOG.info("get blog list for index page fail, parameter invalid, type={}", type);
            return new ResultDO<List<BlogDTO>>(false, ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID,
                null);
        }
        List<BlogDTO> blogDTOs = new ArrayList<>();
        // 最多显示9个
        List<BlogDO> blogDOs = blogDAO.getBlogListByType(type, 9);
        if (blogDOs.size() > 0) {
            for (BlogDO blogDO : blogDOs) {
                BlogDTO blogDTO = new BlogDTO();
                blogDTO.setBlogId(blogDO.getId());
                blogDTO.setBlogTitle(blogDO.getTitle());
                blogDTOs.add(blogDTO);
            }
        }
        LOG.info("get blog list for index page success, type={}, blog lsit={}", type, blogDTOs);
        return new ResultDO<List<BlogDTO>>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS, blogDTOs);
    }

    @Override
    public ResultDO<BlogDetailDTO> getBlogDetail(Long blogId) {
        if (blogId <= 0) {
            LOG.error("get blog detail fail, parameter invalid, blog id={}", blogId);
            return new ResultDO<BlogDetailDTO>(false, ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID,
                null);
        }
        BlogDO blogDO = blogDAO.getBlogDOById(blogId);
        // 不为空且状态为已发布
        BlogDetailDTO blogDetailDTO = getBlogDetailDTO(blogDO);
        if (blogDetailDTO == null) {
            LOG.error("get blog detail fail, blogDetailDTO is null, blogId={}", blogId);
            return new ResultDO<BlogDetailDTO>(false, ResultCode.DB_ERROR, ResultCode.MSG_DB_ERROR, null);
        }
        LOG.info("get blog detail success, blog id={}, blogDetailDTO={}", blogId, blogDetailDTO);
        return new ResultDO<BlogDetailDTO>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS, blogDetailDTO);
    }

    @Override
    public ResultDO<PageDO> getBlogPageByTag(String tagName, int page) {
        if (CommonUtils.validKeyword(tagName) == false || page <= 0) {
            LOG.error("get blog page by tag fail, parameter invalid, tage name={}, page={}", tagName, page);
            return new ResultDO<PageDO>(false, ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID, null);
        }
        TagDO tagDO = tagDAO.getTagDOByName(tagName);
        if (tagDO != null) {
            Map<String, Object> param = new HashMap<>();
            param.put("page", page);
            param.put("limit", 9);
            param.put("tagId", tagDO.getId());
            PageQueryUtil pageQueryUtil = new PageQueryUtil(param);
            List<BlogDO> blogDOList = blogDAO.getBlogsPageByTagId(pageQueryUtil);
            List<BlogListDTO> blogListDTOs = getBlogListDTOsByBlogs(blogDOList);
            int total = blogDAO.getTotalBlogsByTagId(pageQueryUtil);
            LOG.info("get blog page by tag success, tag name={}, page={}", tagName, page);
            return new ResultDO<PageDO>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS,
                new PageDO(total, pageQueryUtil.getLimit(), pageQueryUtil.getPage(), blogListDTOs));
        }
        LOG.error("get blog page by tag fail, tag is null, tag name={}, page={}", tagName, page);
        return new ResultDO<PageDO>(false, ResultCode.DB_ERROR, ResultCode.MSG_DB_ERROR);
    }

    @Override
    public ResultDO<PageDO> getBlogsPageByCategory(String categoryName, int page) {
        if (CommonUtils.validKeyword(categoryName) == false || page <= 0) {
            LOG.error("get blogs page by category fail, parameter invalid, category name={}， page={}", categoryName,
                page);
            return new ResultDO<PageDO>(false, ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID, null);
        }
        CategoryDO categoryDO = categoryDAO.getCategoryDOByName(categoryName);
        if (ConstantHolder.DEFAULT_CATEGORY.equals(categoryName) && categoryDO == null) {
            categoryDO = new CategoryDO();
            categoryDO.setId(ConstantHolder.ID_OF_DEFAULT_CATEGORY);
        }
        if (categoryDO != null && page > 0) {
            Map<String, Object> param = new HashMap<>();
            param.put("page", page);
            param.put("limit", 9);
            param.put("categoryId", categoryDO.getId());
            param.put("status", BlogStatus.RELEASED);
            PageQueryUtil pageQueryUtil = new PageQueryUtil(param);
            List<BlogDO> blogDOList = blogDAO.getBlogList(pageQueryUtil);
            List<BlogListDTO> blogListDTOList = getBlogListDTOsByBlogs(blogDOList);
            int total = blogDAO.getTotalBlogDOs(pageQueryUtil);
            LOG.info("get blogs page by category success, category name={}, page={}", categoryName, page);
            return new ResultDO<PageDO>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS,
                new PageDO(total, pageQueryUtil.getLimit(), pageQueryUtil.getPage(), blogListDTOList));
        }
        LOG.error("get blogs page by category fail, category is null, category name={}, page={}", categoryName, page);
        return new ResultDO<PageDO>(false, ResultCode.DB_ERROR, ResultCode.MSG_DB_ERROR, null);
    }

    @Override
    public ResultDO<PageDO> getBlogsPageBySearch(String keyword, int page) {
        if (CommonUtils.validKeyword(keyword) == false || page <= 0) {
            LOG.error("get blogs page by search fail, parameter invalid, keyword={}, page={}", keyword, page);
            return new ResultDO<PageDO>(false, ResultCode.PARAMETER_INVALID, ResultCode.MSG_PARAMETER_INVALID, null);
        }
        Map<String, Object> param = new HashMap<>();
        param.put("page", page);
        param.put("limit", 9);
        param.put("keyword", keyword);
        param.put("status", BlogStatus.RELEASED);
        PageQueryUtil pageQueryUtil = new PageQueryUtil(param);
        List<BlogDO> blogDOList = blogDAO.getBlogList(pageQueryUtil);
        List<BlogListDTO> blogListDTOList = getBlogListDTOsByBlogs(blogDOList);
        int total = blogDAO.getTotalBlogDOs(pageQueryUtil);
        LOG.info("get blogs page by search success, keyword={}, page={}", keyword, page);
        return new ResultDO<PageDO>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS,
            new PageDO(total, pageQueryUtil.getLimit(), pageQueryUtil.getPage(), blogListDTOList));
    }

    @Override
    public ResultDO<BlogDetailDTO> getBlogDetailBySubUrl(String subUrl) {
        BlogDO blogDO = blogDAO.getBlogDOBySubUrl(subUrl);
        // 不为空且已发布
        BlogDetailDTO blogDetailDTO = getBlogDetailDTO(blogDO);
        if (blogDetailDTO != null) {
            LOG.info("get blog detail by sub url success, sub url={}, blotDetailDto={}", subUrl, blogDetailDTO);
            return new ResultDO<BlogDetailDTO>(true, ResultCode.SUCCESS, ResultCode.MSG_SUCCESS, blogDetailDTO);
        }
        LOG.error("get blog detail by sub url fail, blogDetailDto is null, sub url={}", subUrl);
        return new ResultDO<BlogDetailDTO>(false, ResultCode.DB_ERROR, ResultCode.MSG_DB_ERROR, null);
    }

    private List<BlogListDTO> getBlogListDTOsByBlogs(List<BlogDO> blogList) {
        List<BlogListDTO> blogListDTOs = new ArrayList<>();
        if (blogList.size() > 0) {
            List<Long> categoryIds = blogList.stream().map(BlogDO::getCategoryId).collect(Collectors.toList());
            Map<Long, String> blogCategoryMap = new HashMap<>();
            if (categoryIds.size() > 0) {
                List<CategoryDO> blogCategories = categoryDAO.getCategoryDOsByCategoryIds(categoryIds);
                if (blogCategories.size() > 0) {
                    blogCategoryMap = blogCategories.stream()
                        .collect(Collectors.toMap(CategoryDO::getId, CategoryDO::getIcon, (key1, key2) -> key2));
                }
            }
            for (BlogDO blogDO : blogList) {
                BlogListDTO blogListDTO = dtoUtil.coverBlogDO2BlogListDTO(blogDO);

                if (blogCategoryMap.containsKey(blogDO.getCategoryId())) {
                    blogListDTO.setBlogCategoryIcon(blogCategoryMap.get(blogDO.getCategoryId()));
                } else {
                    blogListDTO.setBlogCategoryId(ConstantHolder.ID_OF_DEFAULT_CATEGORY);
                    blogListDTO.setBlogCategoryName(ConstantHolder.DEFAULT_CATEGORY);
                    blogListDTO.setBlogCategoryIcon("/admin/dist/img/category/00.png");
                }
                blogListDTOs.add(blogListDTO);
            }
        }
        return blogListDTOs;
    }

    private BlogDetailDTO getBlogDetailDTO(BlogDO blogDO) {
        if (blogDO != null && blogDO.getStatus() == BlogStatus.RELEASED) {
            // 增加浏览量
            blogDO.setViews(blogDO.getViews() + 1);
            blogDAO.update(blogDO);
            BlogDetailDTO blogDetailDTO = dtoUtil.coverBlogDO2BlogDetailDTO(blogDO);
            blogDetailDTO.setBlogContent(MarkDownUtil.mdToHtml(blogDetailDTO.getBlogContent()));
            CategoryDO blogCategory = categoryDAO.getCategoryDOById(blogDO.getCategoryId());
            if (blogCategory == null) {
                blogCategory = new CategoryDO();
                blogCategory.setId(ConstantHolder.ID_OF_DEFAULT_CATEGORY);
                blogCategory.setName(ConstantHolder.DEFAULT_CATEGORY);
                blogCategory.setIcon("/admin/dist/img/category/00.png");
            }
            // 分类信息
            blogDetailDTO.setBlogCategoryIcon(blogCategory.getIcon());
            if (!StringUtils.isBlank(blogDO.getTags())) {
                // 标签设置
                List<String> tags = Arrays.asList(blogDO.getTags().split(","));
                blogDetailDTO.setBlogTags(tags);
            }
            // 设置评论数
            Map<String, Object> params = new HashMap<>();
            params.put("blogId", blogDO.getId());
            params.put("commentStatus", CommentStatus.REVIEWED);// 过滤审核通过的数据
            blogDetailDTO.setCommentCount(commentDAO.getTotalBlogComments(params));
            return blogDetailDTO;
        }
        return null;
    }

}
